# 高通QSDK 驱动roam过程分析 ## wpa的roam命令底层驱动处理过程 ``` nl80211_connect ----------------------来自nl80211_ops cfg80211_connect rdev_connect = wlan_cfg80211_connect ---------------来自wlan_cfg80211_ops osif_cm_connect ucfg_cm_start_connect cm_connect_start_req status = cm_sm_deliver_event(vdev, WLAN_CM_SM_EV_CONNECT_REQ, sizeof(*connect_req), connect_req); //发送事件到链接状态机 ``` - 在应用层调用`wpa_cli -p /var/run/wpa_supplicant-ath0 -iath0 roam 00:0f:ff:01:40:12`这样的漫游命令后,wpa应用程序会通过一些列调用到`wpa_driver_nl80211_try_connect`函数中(具体过程参见《todo》),发送命令为`NL80211_CMD_CONNECT`的nl消息到底层驱动; - 底层驱动收到消息后,会调用`qca/src/linux-4.4/net/wireless/nl80211.c/static const struct genl_ops nl80211_ops[]` 中定义的对应处理函数`nl80211_connect`。 下面我们具体分析下`qca/src/linux-4.4/net/wireless/nl80211.c/nl80211_connect`函数 ## nl80211_connect函数分析 ```c static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_connect_params connect; struct wiphy *wiphy; struct cfg80211_cached_keys *connkeys = NULL; int err; // 构建链接参数,把nl消息里面的应用层传过来内容赋值到connect 里面 memset(&connect, 0, sizeof(connect)); // 这里只是演示了部分赋值代码,具体请看源码 if (info->attrs[NL80211_ATTR_IE]) { connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]); connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } if (info->attrs[NL80211_ATTR_MAC]) connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); else if (info->attrs[NL80211_ATTR_MAC_HINT]) connect.bssid_hint = nla_data(info->attrs[NL80211_ATTR_MAC_HINT]); connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); // 赋值完后,调用qca/src/linux-4.4/net/wireless/sme.c/cfg80211_connect, err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL); return err; } ``` ### cfg80211_connect函数分析 qca/src/linux-4.4/net/wireless/sme.c/cfg80211_connect函数原型如下: ```c /* * API calls for nl80211/wext compatibility code */ int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, struct cfg80211_cached_keys *connkeys, const u8 *prev_bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; // 此处省略无用代码 memcpy(wdev->ssid, connect->ssid, connect->ssid_len); wdev->ssid_len = connect->ssid_len; if (!rdev->ops->connect){ err = cfg80211_sme_connect(wdev, connect, prev_bssid); }else{ // 这里会走这个分支,我们下面分析该函数qca/src/linux-4.4/net/wireless/rdev-ops.h/rdev_connect 函数 err = rdev_connect(rdev, dev, connect); } return 0; } ``` #### rdev_connect函数分析 ```c static inline int rdev_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *sme) { int ret; trace_rdev_connect(&rdev->wiphy, dev, sme); // 这里有个回调connect,下面分析如何注册,和里面的内容为什么内容,这里 rdev->ops->connect == wlan_cfg80211_connect 后面分析 ret = rdev->ops->connect(&rdev->wiphy, dev, sme); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } ``` 1. 注册`rdev->ops`地方`qca-wifi-g431c69b42e38-dirty/os/linux/src/ieee80211_cfg80211.c/ieee80211_cfg80211_radio_attach`函数 ``` __ol_ath_attach { #if UMAC_SUPPORT_CFG80211 if (ic->ic_cfg80211_config) { ic->ic_cfg80211_radio_attach(osdev->device, pdev_netdev, ic); // 这里的 ic_cfg80211_radio_attach == ieee80211_cfg80211_radio_attach } #endif } ieee80211_cfg80211_radio_attach wiphy = wlan_cfg80211_wiphy_alloc(&wlan_cfg80211_ops, sizeof(struct cfg80211_context)); wiphy = wiphy_new(ops, priv_size); return wiphy_new_nm(ops, sizeof_priv, NULL); ``` - `qca-wifi-g431c69b42e38-dirty-unified-profile/qca-wifi-g431c69b42e38-dirty/os/linux/src/ieee80211_cfg80211.c/wlan_cfg80211_ops`内容如下: ```c /** * struct cfg80211_ops - cfg80211_ops * * @add_virtual_intf: Add virtual interface * @del_virtual_intf: Delete virtual interface * @change_virtual_intf: Change virtual interface * @change_station: Change station * @add_beacon: Add beacon in sap mode * @del_beacon: Delete beacon in sap mode * @set_beacon: Set beacon in sap mode * @start_ap: Start ap * @change_beacon: Change beacon * @stop_ap: Stop ap * @change_bss: Change bss * @add_key: Add key * @get_key: Get key * @del_key: Delete key * @set_default_key: Set default key * @set_channel: Set channel * @scan: Scan * @connect: Connect * @disconnect: Disconnect * @join_ibss = Join ibss * @leave_ibss = Leave ibss * @set_wiphy_params = Set wiphy params * @set_tx_power = Set tx power * @get_tx_power = get tx power * @remain_on_channel = Remain on channel * @cancel_remain_on_channel = Cancel remain on channel * @mgmt_tx = Tx management frame * @mgmt_tx_cancel_wait = Cancel management tx wait * @set_default_mgmt_key = Set default management key * @set_txq_params = Set tx queue parameters * @get_station = Get station * @set_power_mgmt = Set power management * @del_station = Delete station * @add_station = Add station * @set_pmksa = Set pmksa * @del_pmksa = Delete pmksa * @flush_pmksa = Flush pmksa * @update_ft_ies = Update FT IEs * @tdls_mgmt = Tdls management * @tdls_oper = Tdls operation * @set_rekey_data = Set rekey data * @sched_scan_start = Scheduled scan start * @sched_scan_stop = Scheduled scan stop * @resume = Resume wlan * @suspend = Suspend wlan * @set_mac_acl = Set mac acl * @testmode_cmd = Test mode command * @set_ap_chanwidth = Set AP channel bandwidth * @dump_survey = Dump survey * @key_mgmt_set_pmk = Set pmk key management * @get_channel = get Channel info */ static struct cfg80211_ops wlan_cfg80211_ops = { .add_virtual_intf = wlan_cfg80211_add_virtual_intf, .del_virtual_intf = wlan_cfg80211_del_virtual_intf, #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) .set_beacon = NULL, #else .start_ap = wlan_cfg80211_start_ap, .change_beacon = wlan_cfg80211_change_beacon, .stop_ap = wlan_cfg80211_stop_ap, #endif .change_bss = wlan_cfg80211_change_bss, .add_key = wlan_cfg80211_add_key, .get_key = wlan_cfg80211_get_key, .del_key = wlan_cfg80211_del_key, .set_default_key = wlan_cfg80211_set_default_key, .scan = wlan_cfg80211_scan_start, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)) .abort_scan = wlan_cfg80211_scan_abort, #endif .connect = wlan_cfg80211_connect, // 连接函数 .disconnect = wlan_cfg80211_disconnect, .join_ibss = NULL, .leave_ibss = NULL, .set_wiphy_params = wlan_cfg80211_set_wiphy_params, .set_tx_power = wlan_cfg80211_set_txpower, .get_tx_power = wlan_cfg80211_get_txpower, .set_default_mgmt_key = wlan_cfg80211_set_default_mgmt_key, .set_default_beacon_key = wlan_cfg80211_set_default_beacon_key, .get_station = wlan_cfg80211_get_station, .set_power_mgmt = wlan_cfg80211_set_power_mgmt, .set_pmksa = NULL, .del_pmksa = NULL, .flush_pmksa = NULL, .update_ft_ies = wlan_cfg80211_update_ft_ies, .change_virtual_intf = wlan_cfg80211_change_virtual_intf, .probe_client = wlan_cfg80211_probe_client, .add_station = wlan_cfg80211_add_station, .del_station = wlan_cfg80211_del_station, .change_station = wlan_cfg80211_change_station, .set_rekey_data = wlan_cfg80211_set_rekey_dataata, .sched_scan_start = wlan_cfg80211_sched_scan_start, .sched_scan_stop = wlan_cfg80211_sched_scan_stop, .set_mac_acl = wlan_cfg80211_set_mac_acl, #ifdef CONFIG_NL80211_TESTMODE /* Callback funtion to receive UTF commands from FTM daemon */ .testmode_cmd = wlan_cfg80211_testmode, #endif .set_antenna = wlan_cfg80211_set_antenna, .get_antenna = wlan_cfg80211_get_antenna, .mgmt_tx_cancel_wait = wlan_cfg80211_mgmt_tx_cancel_wait, .mgmt_tx = wlan_cfg80211_mgmt_tx, .set_txq_params = wlan_cfg80211_set_txq_params, .tdls_mgmt = NULL, .remain_on_channel = wlan_cfg80211_remain_on_channel, .tdls_oper = NULL, .cancel_remain_on_channel = wlan_cfg80211_cancel_remain_on_channel, .dump_survey = wlan_hdd_cfg80211_dump_survey, .channel_switch = wlan_cfg80211_channel_switch, .set_ap_chanwidth = wlan_cfg80211_set_ap_chanwidth, .get_channel = wlan_cfg80211_get_channel, #if ATH_SUPPORT_HS20 .set_qos_map = wlan_cfg80211_set_qos_map, #endif #if UMAC_SUPPORT_WPA3_STA .external_auth = wlan_cfg80211_external_auth, #endif #ifdef WLAN_SUPPORT_FILS .set_fils_aad = wlan_cfg80211_set_fils_aad, #endif /* WLAN_SUPPORT_FILS */ }; ``` 由上面分析得到 `.connect = wlan_cfg80211_connect` 进而确定了 `rdev->ops->connect==wlan_cfg80211_connect` - 下面重点分析`wlan_cfg80211_connect`函数 ##### wlan_cfg80211_connect 函数分析 `qca-wifi-g431c69b42e38-dirty/os/linux/src/ieee80211_cfg80211.c/wlan_cfg80211_connect`函数内容如下: ```c int wlan_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_connect_params *req) { // 这里省略了一些函数内容,我们重点关注过程,具体内容请看函数原型 if (vap->iv_roam.iv_roaming) { // 因为是roam命令 故而这里 走这个分支 WIM_LOG_DEBUG("wlan_cfg80211_connect"); wlan_vdev_get_bss_peer_mac(vap->vdev_obj, &conn_param.prev_bssid); osif_vap_roam(ndev); // 终于到osif层了 osif_cm_connect(ndev, vap->vdev_obj, req, &conn_param); } else { osif_vap_pre_init(ndev, 0); osif_cm_connect(ndev, vap->vdev_obj, req, &conn_param); } /* Restore bssid */ req->bssid = old_bssid; return 0 ; } ``` ``` osif_cm_connect ucfg_cm_start_connect cm_connect_start_req status = cm_sm_deliver_event(vdev, WLAN_CM_SM_EV_CONNECT_REQ, sizeof(*connect_req), connect_req); //发送事件到链接状态机 在往下就是状态机了 ```